use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;

use crate::get_constant_info_from_vm_class;
use crate::heap::vm_heap::VMHeap;
use crate::instruction::instruction::Instruction;
use crate::class::vm_access_flags::{is_final, is_static};
use crate::class::vm_class_constant_info::VMClassConstantInfoEnum;
use crate::vm_thread::{VMFrame, VMThread};
use crate::vm_val::VMValEnum;

pub struct getfield {
    pub index: u16,
}


impl Instruction for getfield {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.index = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let ro_frame = frame.borrow();
        let mut field_ref = get_constant_info_from_vm_class!(
            ro_frame.current_vm_class, self.index, VMClassConstantInfoFieldRef);

        field_ref.init(thread);
        let vm_class_field = field_ref.vm_class_field.clone().unwrap();
        let ro_field = vm_class_field.borrow();
        if is_static(ro_field.access_flags) {
            panic!("不允许对静态变量执行此指令")
        }

        let mut operand_stack = ro_frame.operand_stack.borrow_mut();
        let obj_ref = {
            let option_val = operand_stack.pop();
            if option_val.is_none() {
                panic!("操作数栈为空");
            }
            match option_val.unwrap() {
                VMValEnum::VMValRef { val } => {
                    val
                }
                _ => { panic!("{},{},{}", file!(), line!(), "参数类型异常") }
            }
        };
        let obj_result = VMHeap::get_vmobject(obj_ref);
        if obj_result.is_err() {
            panic!("{},{},{}", file!(), line!(), obj_result.err().unwrap());
        }
        let obj = obj_result.unwrap();
        let ro_slots = obj.slots.borrow();
        let result = ro_slots.get(ro_field.slot_id as usize);
        if result.is_none() {
            panic!("{},{},{}", file!(), line!(), "字段获取异常");
        }
        operand_stack.push(result.unwrap().clone())
    }

    fn to_string(&self) -> String {
        String::from("getfield")
    }
}